package scatter.file.rest.cloudstorage.controller;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.URLUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import scatter.common.rest.controller.SuperController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
import java.util.Optional;

import scatter.common.rest.exception.BusinessException;
import scatter.common.rest.tools.PathTool;
import scatter.file.pojo.po.File;
import scatter.file.rest.FileConfiguration;
import scatter.file.rest.cloudstorage.config.CloudStorageConfig;
import scatter.file.rest.cloudstorage.config.impl.AbstractCloudStorageConfig;
import scatter.file.rest.cloudstorage.config.impl.LocalStorageConfig;
import scatter.file.rest.cloudstorage.form.UploadForm;
import scatter.file.rest.cloudstorage.service.ApiCloudStorageService;
import scatter.file.rest.cloudstorage.service.ICloudStorageUploadFileTypeDictResolver;
import scatter.file.rest.cloudstorage.vo.UploadVo;
import scatter.file.rest.mapstruct.FileMapStruct;
import scatter.file.rest.service.IFileService;

/**
 * 云存储上传控制器
 * 本地存储访问，未添加访问方法，请配合nginx等访问
 * Created by yangwei
 * Created at 2020/3/11 17:43
 */
@Slf4j
@RestController
@RequestMapping(FileConfiguration.CONTROLLER_BASE_PATH + CloudStorageController.API_REQUEST_MAPPING)
@Api(tags = "云存储文件上传下载接口")
public class CloudStorageController extends SuperController {
    public static final String API_REQUEST_MAPPING = "/file/cloudstorage";
    public static final String API_DOWNLOAD = "/download";
    public static final String API_DOWNLOAD_PREFIX = API_REQUEST_MAPPING + API_DOWNLOAD;

    @Autowired
    private List<ApiCloudStorageService> apiCloudStorageServices;

    @Autowired
    private List<CloudStorageConfig> cloudStorageConfigs;
    @Autowired
    private IFileService iFileService;
    @Autowired(required = false)
    private ICloudStorageUploadFileTypeDictResolver iCloudStorageUploadFileTypeDictResolver;
    /**
     * 当前使用的存储上传实现，参考
     * @see CloudStorageConfig
     * 的实现类的值配置
     */
    @Value("${scatter.file.cloudstorage.upload-type:local}")
    private String type;

    /**
     * 文件上传 通用上传接口
     *
     * @param uploadForm
     * @return 返回文件的绝对路径
     * @throws IOException
     */
    //@PreAuthorize("hasAuthority('user')")
    @ApiOperation("文件上传")
    @PostMapping(value = "/upload",consumes = "multipart/*",headers = "content-type=multipart/form-data")
    @ResponseStatus(HttpStatus.CREATED)
    public UploadVo upload(@Valid UploadForm uploadForm) throws IOException {
        log.info("文件上传开始，type={}",type);

        MultipartFile file = uploadForm.getFile();
        String originalFilename = file.getOriginalFilename();
        InputStream inputStream = file.getInputStream();
        String extension = originalFilename.substring(originalFilename.lastIndexOf("."));
        String newFileName = UUID.randomUUID() + originalFilename;
        String subPath =  Optional.ofNullable(uploadForm.getPath()).orElse("") + "/" + DateUtil.date().toDateStr() + "/" + newFileName;

        String result = null;
        for (ApiCloudStorageService apiCloudStorageService : apiCloudStorageServices) {
            if (apiCloudStorageService.support(type)) {
                result = apiCloudStorageService.upload(inputStream, subPath);
                break;
            }
        }
        IoUtil.close(inputStream);
        // 构建文件数据并存储
        CloudStorageConfig cloudStorageConfig = cloudStorageConfigs.stream().filter(item -> isEqual(item.uploadType(), type)).findFirst().orElse(null);
        File dbFile = new File();
        dbFile.setFileSize(String.valueOf(file.getSize()));
        dbFile.setName(newFileName);
        dbFile.setExtension(extension);
        dbFile.setObjectName(PathTool.concat(cloudStorageConfig.prefix(),subPath));
        dbFile.setPath(uploadForm.getPath());
        if (isEqual(type, getLocalStorageConfig().uploadType())) {
            dbFile.setRootPath(((LocalStorageConfig) getLocalStorageConfig()).getLocation());
        }else {
            dbFile.setRootPath(cloudStorageConfig.domain());
        }
        dbFile.setOriginName(originalFilename);
        dbFile.setStorageType(type);

        if (cloudStorageConfig != null && cloudStorageConfig instanceof AbstractCloudStorageConfig) {
            dbFile.setStorageTypeMemo(((AbstractCloudStorageConfig) cloudStorageConfig).getUploadTypeMemo());
        }
        dbFile.setUrl(result);
        if (iCloudStorageUploadFileTypeDictResolver != null) {
            dbFile.setTypeDictId(iCloudStorageUploadFileTypeDictResolver.resolve(uploadForm.getFileTypeDictValue(), extension));
        }

        iFileService.save(dbFile);

        log.info("文件上传结束,url={}",result);
        UploadVo uploadVo =  FileMapStruct.INSTANCE.fileToUploadVo(dbFile);

        return uploadVo;
    }

    /**
     * 下载
     * @param request
     */
    @GetMapping(API_DOWNLOAD + "/**")
    public void download(HttpServletRequest request, HttpServletResponse response,String objectName, String uploadType) throws Throwable {
        String filePath = getObjectName(request, objectName);
        if (isStrEmpty(uploadType)) {
            uploadType = type;
        }
        byte[] result = null;
        for (ApiCloudStorageService apiCloudStorageService : apiCloudStorageServices) {
            if (apiCloudStorageService.support(uploadType)) {
                result = apiCloudStorageService.download(filePath);
                break;
            }
        }
        if (result != null) {
            InputStream inputStream = new ByteArrayInputStream(result);
            OutputStream stream = response.getOutputStream();
            IoUtil.copy(inputStream,stream);
            IoUtil.close(inputStream);
            IoUtil.close(stream);
        }
    }
    /**
     * 删除
     * @param request
     */
    @PreAuthorize("hasAuthority('user')")
    @DeleteMapping( API_DOWNLOAD + "/**")
    @ResponseStatus(HttpStatus.NO_CONTENT)
    public void delete(HttpServletRequest request,String objectName, String uploadType){

        String filePath = getObjectName(request, objectName);
        if (isStrEmpty(uploadType)) {
            uploadType = type;
        }
        boolean result = false;
        for (ApiCloudStorageService apiCloudStorageService : apiCloudStorageServices) {
            if (apiCloudStorageService.support(uploadType)) {
                result = apiCloudStorageService.delete(filePath);
                break;
            }
        }
        if (!result) {
            throw new BusinessException("删除失败或文件不存在");
        }
        // 删除文件数据
        boolean b = iFileService.deleteByObjectNameAndStorageType(filePath, uploadType);
        if (!b) {
            // 再获取一次数据确认数据是否存在
            File byObjectNameAndStorageType = iFileService.getByObjectNameAndStorageType(filePath, uploadType);
            if (byObjectNameAndStorageType != null) {
                log.error("上传文件删除数据失败，objectName={},uploadType={}",objectName,uploadType);
            }
        }
    }

    /**
     * 获取文件对象名称
     * @param request
     * @param objectName
     * @return
     */
    private String getObjectName(HttpServletRequest request, String objectName) {
        String requestURI = request.getRequestURI();
        requestURI = URLUtil.decode(requestURI);
        String filePath = requestURI.replaceFirst(getPrefix(),"");
        if (isEqual(filePath, "") || isEqual(filePath, "/") || isEqual(filePath, "/**")) {
            filePath = objectName;
            Assert.hasText(objectName,"objectName不能为空或直接拼接在请求地址上");
        }
        return filePath;
    }
    /**
     * 获取本地存储配置
     * @return
     */
    private CloudStorageConfig getLocalStorageConfig(){
        return cloudStorageConfigs.stream().filter(item -> item.isLocalStorage()).findFirst().orElseThrow(()->new RuntimeException("没有找到本地存储配置，请检查配置"));
    }

    /**
     * 获取下载前缀
     * @return
     */
    public String getPrefix(){
        return API_DOWNLOAD_PREFIX;
    }
}
